/****************************************************************************
 **
 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
 ** This file is part of the demonstration applications of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** No Commercial Usage
 ** This file contains pre-release code and may not be distributed.
 ** You may use this file in accordance with the terms and conditions
 ** contained in the Technology Preview License Agreement accompanying
 ** this package.
 **
 ** GNU Lesser General Public License Usage
 ** Alternatively, this file may be used under the terms of the GNU Lesser
 ** General Public License version 2.1 as published by the Free Software
 ** Foundation and appearing in the file LICENSE.LGPL included in the
 ** packaging of this file.  Please review the following information to
 ** ensure the GNU Lesser General Public License version 2.1 requirements
 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
 ** rights.  These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
 ** If you have questions regarding the use of this file, please contact
 ** Nokia at qt-info@nokia.com.
 **
 **
 **
 **
 **
 **
 **
 **
 ** $QT_END_LICENSE$
 **
 **  TODO Inserire i codici key()
 **  TODO Inserire nelle api il nome del paese
 **  TODO Widget contenente QFormLayout (http://wiki.forum.nokia.com/index.php/How_to_create_a_form_using_QFormLayout_in_Qt_for_Symbian)
 **  NODO [Ok] Inserire segno sulla mappa
 **  NODO [Ok] Inserire cerchio di approssimazione
 **  TODO Traffico
 **  TODO Settings
 **  TODO Wizard (dopo aver fatto il settings)
 **  TODO Icona
 **  TODO Eliminare cache a piacere
 **  TODO [No] Impostare il comando riposizionamento alla cella (verificare perche' non si centra con setCenter)
 **  TODO Rifare il QSearch DECENTEMENTE
 ****************************************************************************/
#warning leggi i to do

#include <QtCore>
#include <QtGui>
#include <QtNetwork>

#if defined (Q_OS_SYMBIAN)
#include "sym_iap_util.h"
#include "XQSysInfo.h"
#include "s60version.h"
#endif

#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

// how long (milliseconds) the user need to hold (after a tap on the screen)
// before triggering the magnifying glass feature
// 701, a prime number, is the sum of 229, 233, 239
// (all three are also prime numbers, consecutive!)
#define HOLD_TIME 701

// maximum size of the magnifier
// Hint: see above to find why I picked this one :)
#define MAX_MAGNIFIER 229

uint qHash(const QPoint& p)
	{
	return p.x() * 17 ^ p.y();
	}

// version
const float cid_version = 0.1;
// max int
const int maxint = 2147483647;
// tile size in pixels
const int tdim = 256;
// padding between icons
const int padding = 5;
// bottom (and global) icon size
const int iconsize = 57;
// bottom (and global) icon size small
const int iconsize_small = 32;
// topbar icon size
const int iconsizebar = 48;
// marker image size
#warning change this value if image size (50x50) changes
const int markerimagesize = 50;

QPointF tileForCoordinate(qreal lat, qreal lng, int zoom)
	{
	qreal zn = static_cast<qreal> (1 << zoom);
	qreal tx = (lng + 180.0) / 360.0;
	qreal ty = (1.0 - log(tan(lat * M_PI / 180.0) + 1.0 / cos(lat * M_PI
			/ 180.0)) / M_PI) / 2.0;
	return QPointF(tx * zn, ty * zn);
	}

qreal longitudeFromTile(qreal tx, int zoom)
	{
	qreal zn = static_cast<qreal> (1 << zoom);
	qreal lat = tx / zn * 360.0 - 180.0;
	return lat;
	}

qreal latitudeFromTile(qreal ty, int zoom)
	{
	qreal zn = static_cast<qreal> (1 << zoom);
	qreal n = M_PI - 2 * M_PI * ty / zn;
	qreal lng = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
	return lng;
	}

class QAnimationLabel : public QWidget
	{
Q_OBJECT
public:
	QAnimationLabel(QString animationPath, QWidget* parent) :
		QWidget(parent)
		{
		init(animationPath, QSize());
		}

	QAnimationLabel(QString animationPath, QSize size, QWidget* parent) :
		QWidget(parent)
		{
		init(animationPath, size);
		}

	virtual ~QAnimationLabel()
		{
		_container->deleteLater();
		_animation->deleteLater();
		}

public slots:
	//![Start Animation]
	void start() 
		{
		/* Let's check if the movie can be started. */
		if (!_animation.isNull() && (_animation->state() == QMovie::NotRunning
				|| _animation->state() == QMovie::Paused))
			{
			/* It can so we'll start the animation... */
			_animation->start();
			/* ...and reveal ourself. */
			setHidden(false);
			}
		}

	//![Stop Animation]
	void stop()
		{
		/* Check if the animation can be stopped. */
		if (!_animation.isNull())
			{
			if (_animation->state() == QMovie::Running)
				{
				/* It can so we'll stop the animation... */
				_animation->stop();
				/* ...and hide. */
				setHidden(true);
				}
			}
		}

private:
	QPointer<QLabel> _container;
	QPointer<QMovie> _animation;

private:
	//![Initialize Class]
	void init(const QString& animationPath, const QSize& size)
		{
		/* We'll create the QMovie for the animation */
		_animation = new QMovie(animationPath);
		QSize s = size;
		/* If the size is empty we'll try to detect it.*/
		if (s.isEmpty())
			{
			/* Go to the first frame.*/
			_animation->jumpToNextFrame();
			/* Take the size from there. */
			s = _animation->currentPixmap().size();
			/* Go back to the beginning. */
			_animation->jumpToFrame(0);
			}
		/* We're not automatically shown, so lets hide. */
		setHidden(true);
		/* We need a container for the QMovie, let's use QLabel */
		_container = new QLabel(this);
		/*
		 * We'll set a fixed size to the QLabel
		 * because we don't want to be resized
		 */
		_container->setFixedSize(s);
		/* To get the movie displayed on the QLabel */
		_container->setMovie(_animation);
		/* To get the QLabel displayed we'll use a layout */
		QVBoxLayout* layout = new QVBoxLayout(this);
		/* Remove the all the extra space. */
		layout->setSpacing(0);
		layout->setMargin(0);
		/* Add the movie container to the layout */
		layout->addWidget(_container);
		/* Set the layout as our layout */
		setLayout(layout);
		/* Set our size fixed. */
		setFixedSize(s);
		}
	};

class SlippyMap : public QObject
	{
Q_OBJECT

public:
	int width;
	int height;
	int zoom;
	qreal latitude;
	qreal longitude;
	QNetworkDiskCache *cache;

	SlippyMap(QObject *parent = 0) :
		QObject(parent), width(400), height(300), zoom(15),
				latitude(41.890197), longitude(12.49229), m_deltaMarker(QPoint(0, 0))
		{
		// m_emptyTile = QPixmap(tdim, tdim);
		// m_emptyTile.fill(Qt::lightGray);
		// Get a dirty background (when map is unloaded yet)
		m_emptyTile = QPixmap(":/data/empty_tile.png");

		// @ !:\private\0xuid\cache
		cache = new QNetworkDiskCache;
		// cache->setCacheDirectory("c:\\cacheDir");
		cache->setCacheDirectory(QDesktopServices::storageLocation(
				QDesktopServices::CacheLocation));
		m_manager.setCache(cache);
		connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this,
				SLOT(handleNetworkData(QNetworkReply*)));
		// QPoint for basic marker
#define half_scrn QPoint(width / 2 - markerimagesize / 2 + markerimagesize / 3, height / 2 - markerimagesize / 2 - markerimagesize)
#define reset_half_scrn QPoint(width / 2, height / 2)
		}

	~SlippyMap()
		{
		// cache->clear(); // If user wants, delete cache
		}

	//![Reset Map Marker and Delta's pan]
	void resetMarker()
		{
		m_deltaMarker = QPoint(0, 0);
		m_marker = half_scrn;
		m_areaMarker = reset_half_scrn;
		}

	//![Get Marker Position]
	QPoint getMarker()
		{
		return m_marker;
		}

	//![Get Area Marker Position]
	QPoint getAreaMarker()
		{
		return m_areaMarker;
		}

	//![Traslate all variables if there was some changes]
	void invalidate()
		{
		if (width <= 0 || height <= 0)
			return;

		QPointF ct = tileForCoordinate(latitude, longitude, zoom);
		qreal tx = ct.x();
		qreal ty = ct.y();

		// top-left corner of the center tile
		int xp = static_cast<int> (width / 2 - (tx - floor(tx)) * tdim);
		int yp = static_cast<int> (height / 2 - (ty - floor(ty)) * tdim);

		// first tile vertical and horizontal
		int xa = (xp + tdim - 1) / tdim;
		int ya = (yp + tdim - 1) / tdim;
		int xs = static_cast<int> (tx) - xa;
		int ys = static_cast<int> (ty) - ya;

		// offset for top-left tile
		m_offset = QPoint(xp - xa * tdim, yp - ya * tdim);

		// move marker
		m_marker = m_deltaMarker + half_scrn;
		m_areaMarker = m_deltaMarker + reset_half_scrn;

		// last tile vertical and horizontal
		int xe = static_cast<int> (tx) + (width - xp - 1) / tdim;
		int ye = static_cast<int> (ty) + (height - yp - 1) / tdim;

		// build a rect
		m_tilesRect = QRect(xs, ys, xe - xs + 1, ye - ys + 1);

		if (m_url.isEmpty())
			download();

		emit updated(QRect(0, 0, width, height));
		}

	//![Paint Map parts or empty parts]
	void render(QPainter *p, const QRect &rect)
		{
		for (int x = 0; x <= m_tilesRect.width(); ++x)
			for (int y = 0; y <= m_tilesRect.height(); ++y)
				{
				QPoint tp(x + m_tilesRect.left(), y + m_tilesRect.top());
				QRect box = tileRect(tp);
				if (rect.intersects(box))
					{
					if (m_tilePixmaps.contains(tp))
						p->drawPixmap(box, m_tilePixmaps.value(tp));
					else
						p->drawPixmap(box, m_emptyTile);
					}
				}
		}

	//![Move map on the screen]
	void pan(const QPoint &delta)
		{
		QPointF dx = QPointF(delta) / qreal(tdim);
		QPointF center = tileForCoordinate(latitude, longitude, zoom) - dx;
		latitude = latitudeFromTile(center.y(), zoom);
		longitude = longitudeFromTile(center.x(), zoom);
		// Update delta marker
		m_deltaMarker += delta;
		invalidate();
		}

	//![Get traffic byte]
	float traffic()
		{
		return traffic_byte;
		}

private slots:

	//![HTTP Connection with webserver]
	//![Opened by QNetworkAccessManager "finished" signal]
	void handleNetworkData(QNetworkReply *reply)
		{
		QImage img;
		QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint();
		QUrl url = reply->url();
		if (!reply->error())
			if (!img.load(reply, 0))
				img = QImage();
		traffic_byte += reply->readAll().size() / 1024;
		reply->deleteLater();
		m_tilePixmaps[tp] = QPixmap::fromImage(img);
		if (img.isNull())
			m_tilePixmaps[tp] = m_emptyTile;
		emit
		updated(tileRect(tp));
		emit
		finished();

		// purge unused spaces
		QRect bound = m_tilesRect.adjusted(-2, -2, 2, 2);
		foreach(QPoint tp, m_tilePixmaps.keys())
				if (!bound.contains(tp))
					m_tilePixmaps.remove(tp);

		download();
		}

	//![Download Map]
	void download()
		{
		QPoint grab(0, 0);
		for (int x = 0; x <= m_tilesRect.width(); ++x)
			for (int y = 0; y <= m_tilesRect.height(); ++y)
				{
				QPoint tp = m_tilesRect.topLeft() + QPoint(x, y);
				if (!m_tilePixmaps.contains(tp))
					{
					grab = tp;
					break;
					}
				}
		if (grab == QPoint(0, 0))
			{
			m_url = QUrl();
			return;
			}

		QString path = "http://tile.openstreetmap.org/%1/%2/%3.png";
		m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y()));
		QNetworkRequest request;
		request.setUrl(m_url);
		request.setRawHeader("User-Agent", "cellID (Qt) " + QString::number(
				cid_version).toAscii());
		request.setAttribute(QNetworkRequest::User, QVariant(grab));
		m_manager.get(request);
		emit started();
		}

signals:
	void updated(const QRect &rect);
	void finished();
	void started();

protected:
	QRect tileRect(const QPoint &tp)
		{
		QPoint t = tp - m_tilesRect.topLeft();
		int x = t.x() * tdim + m_offset.x();
		int y = t.y() * tdim + m_offset.y();
		return QRect(x, y, tdim, tdim);
		}

private:
	QPoint m_offset;
	QRect m_tilesRect;
	QPixmap m_emptyTile;
	QHash<QPoint, QPixmap> m_tilePixmaps;
	QNetworkAccessManager m_manager;
	QUrl m_url;
	float traffic_byte;
	QPoint m_marker, m_areaMarker;
	QPoint m_deltaMarker;
	};

class QLightMaps : public QWidget
	{
Q_OBJECT

public:
	QLightMaps(QWidget *parent = 0) :
		QWidget(parent), pressed(false), snapped(false), zoomed(false), invert(false), m_approx(200)
		{
		m_normalMap = new SlippyMap(this);
		m_normalMap->resetMarker();
		m_largeMap = new SlippyMap(this);
		m_largeMap->resetMarker();
		connect(m_normalMap, SIGNAL(updated(QRect)), SLOT(updateMap(QRect)));
		connect(m_largeMap, SIGNAL(updated(QRect)), SLOT(update()));
		markerPixmap = QPixmap(":/data/marker.png");
		}

	//![Set the map at this coords]
	void setCenter(qreal lat, qreal lng, int approx = 2000)
		{
		m_normalMap->latitude = lat;
		m_normalMap->longitude = lng;
		m_normalMap->resetMarker();
		m_normalMap->invalidate();
#warning migliorare approx in base allo zoom visualizzato
		m_approx = approx / 10;
		m_largeMap->invalidate();
		m_largeMap->resetMarker();
		}

	//![Get if map is zoomed]
	bool zoom()
		{
		return zoomed;
		}

public slots:
	//![Get traffic byte]
	float getTraffic()
		{
		return m_normalMap->traffic() + m_largeMap->traffic();
		}

	//![Switch between Day and Night Mode]
	void toggleNightMode()
		{
		invert = !invert;
		update();
		}

	//![Activate or Deactivate Zoom on the map]
	void setZoom(bool zoom)
		{
		dragPos = QPoint(width() / 2, height() / 2);
		if (zoom)
			activateZoom();
		else
			{
			zoomed = false;
			update();
			}
		}

private slots:
	//![Update Map]
	void updateMap(const QRect &r)
		{
		update(r);
		}

protected:

	//![Activate Zoom]
	void activateZoom()
		{
		zoomed = true;
		tapTimer.stop();
		m_largeMap->zoom = m_normalMap->zoom + 1;
		m_largeMap->width = m_normalMap->width * 2;
		m_largeMap->height = m_normalMap->height * 2;
		m_largeMap->latitude = m_normalMap->latitude;
		m_largeMap->longitude = m_normalMap->longitude;
		m_largeMap->invalidate();
		update();
		}

	void resizeEvent(QResizeEvent *)
		{
		m_normalMap->width = width();
		m_normalMap->height = height();
		m_normalMap->invalidate();
		m_largeMap->width = m_normalMap->width * 2;
		m_largeMap->height = m_normalMap->height * 2;
		m_largeMap->invalidate();
		}

	void paintEvent(QPaintEvent *event)
		{
		QPainter p;
		p.begin(this);
		m_normalMap->render(&p, event->rect());
		p.setPen(Qt::black);
#if defined(Q_OS_SYMBIAN)
		QFont font = p.font();
		font.setPixelSize(13);
		p.setFont(font);
#endif
		p.drawText(rect(), Qt::AlignBottom | Qt::TextWordWrap,
				"Map data OpenStreetMap.org - www.cellid.eu");
		// Marker
		p.setRenderHint(QPainter::Antialiasing);
		p.setBrush(QColor(110, 110, 255, 100));
		p.setPen(QColor(0, 0, 205));
		p.drawEllipse(m_normalMap->getAreaMarker(), m_approx / 2, m_approx / 2);
		p.drawPixmap(m_normalMap->getMarker(), markerPixmap);
		p.end();

		if (zoomed)
			{
			int dim = qMin(width(), height());
			int magnifierSize = qMin(MAX_MAGNIFIER, dim * 2 / 3);
			int radius = magnifierSize / 2;
			int ring = radius - 15;
			QSize box = QSize(magnifierSize, magnifierSize);

			// reupdate our mask
			if (maskPixmap.size() != box)
				{
				maskPixmap = QPixmap(box);
				maskPixmap.fill(Qt::transparent);

				QRadialGradient g;
				g.setCenter(radius, radius);
				g.setFocalPoint(radius, radius);
				g.setRadius(radius);
				g.setColorAt(1.0, QColor(255, 255, 255, 0));
				g.setColorAt(0.5, QColor(128, 128, 128, 255));

				QPainter mask(&maskPixmap);
				mask.setRenderHint(QPainter::Antialiasing);
				mask.setCompositionMode(QPainter::CompositionMode_Source);
				mask.setBrush(g);
				mask.setPen(Qt::NoPen);
				mask.drawRect(maskPixmap.rect());
				mask.setBrush(QColor(Qt::transparent));
				mask.drawEllipse(g.center(), ring, ring);
				mask.end();
				}

			QPoint center = dragPos - QPoint(0, radius);
			center = center + QPoint(0, radius / 2);
			QPoint corner = center - QPoint(radius, radius);

			QPoint xy = center * 2 - QPoint(radius, radius);

			// only set the dimension to the magnified portion
			if (zoomPixmap.size() != box)
				{
				zoomPixmap = QPixmap(box);
				zoomPixmap.fill(Qt::lightGray);
				}
			if (true)
				{
				QPainter p(&zoomPixmap);
				p.translate(-xy);
				m_largeMap->render(&p, QRect(xy, box));
				p.end();
				}

			QPainterPath clipPath;
			clipPath.addEllipse(center, ring, ring);

			QPainter p(this);
			p.setRenderHint(QPainter::Antialiasing);
			p.setClipPath(clipPath);
			p.drawPixmap(corner, zoomPixmap);
			p.setClipping(false);
			p.drawPixmap(corner, maskPixmap);
			p.setPen(Qt::gray);
			p.drawPath(clipPath);
			}

#warning night mode non funzionante su N8
		if (invert)
			{
			QPainter p(this);
			p.setCompositionMode(QPainter::CompositionMode_Difference);
			p.fillRect(event->rect(), Qt::white);
			p.end();
			}
		}

	void timerEvent(QTimerEvent *)
		{
		if (!zoomed)
			activateZoom();
		update();
		}

	void mousePressEvent(QMouseEvent *event)
		{
		if (event->buttons() != Qt::LeftButton)
			return;
		pressed = snapped = true;
		pressPos = dragPos = event->pos();
		tapTimer.stop();
		tapTimer.start(HOLD_TIME, this);
		}

	void mouseMoveEvent(QMouseEvent *event)
		{
		if (!event->buttons())
			return;
		if (!zoomed)
			{
			if (!pressed || !snapped)
				{
				QPoint delta = event->pos() - pressPos;
				pressPos = event->pos();
				m_normalMap->pan(delta);
				return;
				}
			else
				{
				const int threshold = 10;
				QPoint delta = event->pos() - pressPos;
				if (snapped)
					{
					snapped &= delta.x() < threshold;
					snapped &= delta.y() < threshold;
					snapped &= delta.x() > -threshold;
					snapped &= delta.y() > -threshold;
					}
				if (!snapped)
					tapTimer.stop();
				}
			}
		else
			{
			dragPos = event->pos();
			update();
			}
		}

	void mouseReleaseEvent(QMouseEvent *)
		{
		zoomed = false;
		update();
		}

	void keyPressEvent(QKeyEvent *event)
		{
		if (!zoomed)
			{
			if (event->key() == Qt::Key_Left)
				m_normalMap->pan(QPoint(20, 0));
			if (event->key() == Qt::Key_Right)
				m_normalMap->pan(QPoint(-20, 0));
			if (event->key() == Qt::Key_Up)
				m_normalMap->pan(QPoint(0, 20));
			if (event->key() == Qt::Key_Down)
				m_normalMap->pan(QPoint(0, -20));
			if (event->key() == Qt::Key_Z || event->key() == Qt::Key_Select)
				{
				dragPos = QPoint(width() / 2, height() / 2);
				activateZoom();
				}
			}
		else
			{
			if (event->key() == Qt::Key_Z || event->key() == Qt::Key_Select)
				{
				zoomed = false;
				update();
				}
			QPoint delta(0, 0);
			if (event->key() == Qt::Key_Left)
				delta = QPoint(-15, 0);
			if (event->key() == Qt::Key_Right)
				delta = QPoint(15, 0);
			if (event->key() == Qt::Key_Up)
				delta = QPoint(0, -15);
			if (event->key() == Qt::Key_Down)
				delta = QPoint(0, 15);
			if (delta != QPoint(0, 0))
				{
				dragPos += delta;
				update();
				}
			}
		}

public:
	SlippyMap *m_normalMap;
	SlippyMap *m_largeMap;
private:
	bool pressed;
	bool snapped;
	QPoint pressPos;
	QPoint dragPos;
	QBasicTimer tapTimer;
	bool zoomed;
	QPixmap zoomPixmap;
	QPixmap maskPixmap;
	QPixmap markerPixmap;
	bool invert;
	int m_approx;
	};

class QSearch : public QWidget
	{
Q_OBJECT

public:
	QSearch(QWidget *parent = 0) :
		QWidget(parent)
		{
		}

	~QSearch()
		{
		}

	//![Get errors, if there is, on discovery gps] 
	bool errors()
		{
		return m_errors;
		}

	QString getMCC()
		{
		return mcc->text();
		}

	QString getMNC()
		{
		return mnc->text();
		}

	QString getLAC()
		{
		return lac->text();
		}

	QString getCID()
		{
		return cid->text();
		}

public slots:
	//![Show Search Dialog]
	void search()
		{
		m_errors = false;
		setWindowFlags(windowFlags() ^ Qt::WindowSoftkeysVisibleHint);
		showFullScreen();
#if defined (Q_OS_SYMBIAN)
		XQSysInfo *sysinfo = new XQSysInfo;
#endif
		dlgMultiLine = new QDialog(this);

		gridLayout = new QGridLayout(dlgMultiLine);

		mcc_l = new QLabel();
		mcc_l->setStyleSheet("background: rgba(0, 0, 0, 0);");
		mcc_l->setText("MCC:");

		mnc_l = new QLabel();
		mnc_l->setStyleSheet("background: rgba(0, 0, 0, 0);");
		mnc_l->setText("MNC:");

		lac_l = new QLabel();
		lac_l->setStyleSheet("background: rgba(0, 0, 0, 0);");
		lac_l->setText("LAC:");

		cid_l = new QLabel();
		cid_l->setStyleSheet("background: rgba(0, 0, 0, 0);");
		cid_l->setText("CID:");

		mcc = new QLineEdit();
		mcc->setInputMethodHints(Qt::ImhDigitsOnly);
		mcc->setMaxLength(16);
		mcc->setAlignment(Qt::AlignRight);

		mnc = new QLineEdit();
		mnc->setInputMethodHints(Qt::ImhDigitsOnly);
		mnc->setMaxLength(16);
		mnc->setAlignment(Qt::AlignRight);

		lac = new QLineEdit();
		lac->setInputMethodHints(Qt::ImhDigitsOnly);
		lac->setMaxLength(16);
		lac->setAlignment(Qt::AlignRight);

		cid = new QLineEdit();
		cid->setInputMethodHints(Qt::ImhDigitsOnly);
		cid->setMaxLength(16);
		cid->setAlignment(Qt::AlignRight);

		QFrame *f = new QFrame();
		f->setFrameStyle(QFrame::HLine | QFrame::Sunken);

		gridLayout->addWidget(mcc_l, 1, 0, 1, 1);
		gridLayout->addWidget(mnc_l, 1, 1, 1, 1);
		gridLayout->addWidget(mcc, 2, 0, 1, 1);
		gridLayout->addWidget(mnc, 2, 1, 1, 1);
		gridLayout->addWidget(f, 3, 0, 1, 2);
		gridLayout->addWidget(lac_l, 4, 0, 1, 1);
		gridLayout->addWidget(cid_l, 4, 1, 1, 1);
		gridLayout->addWidget(lac, 5, 0, 1, 1);
		gridLayout->addWidget(cid, 5, 1, 1, 1);

		buttonBox = new QDialogButtonBox();
		buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
		buttonBox->setOrientation(Qt::Horizontal);
		buttonBox->setStandardButtons(QDialogButtonBox::Cancel
				| QDialogButtonBox::Save);

		gridLayout->addWidget(buttonBox, 3, 0, 1, 1);

		connect(buttonBox, SIGNAL(accepted()), this, SLOT(accepted()));
		connect(buttonBox, SIGNAL(rejected()), this, SLOT(rejected()));

#if defined (Q_OS_SYMBIAN)
		mcc->setText(sysinfo->mcc());
		mnc->setText(sysinfo->mnc());
		lac->setText(sysinfo->lac());
		cid->setText(sysinfo->cid());
#endif

		dlgMultiLine->setWindowModality(Qt::WindowModal);
		dlgMultiLine->show();
		}

	//![Opened if clicked Left Button]
	void accepted()
		{
		m_errors = false;
		dlgMultiLine->close();
		emit closed();
		}

	//![Opened if clicked Right Button]
	void rejected()
		{
		m_errors = true;
		mcc->setText("");
		mnc->setText("");
		lac->setText("");
		cid->setText("");
		dlgMultiLine->close();
		emit closed();
		}

signals:
	void closed();

private:
	QLineEdit *mcc, *mnc, *lac, *cid;
	QLabel *mcc_l, *mnc_l, *lac_l, *cid_l;
	QDialogButtonBox *buttonBox;
	QDialog *dlgMultiLine;
	QGridLayout *gridLayout;
	bool m_errors;
	};

class MapZoom : public QMainWindow
	{
Q_OBJECT

private:
	QLightMaps *map;
	QPushButton *nightday, *infoGSM, *zoom;
	QLabel *opname;
#if defined (Q_OS_SYMBIAN)
	XQSysInfo *sysinfo;
#endif
	QWidget *topbar;
	QNetworkAccessManager m_manager;
	QAnimationLabel *loading, *searchloading;
	s60version s60ver; // float
	int s60v;
	QSearch *s;
	QString version;

public:
	MapZoom() :
		QMainWindow(0)
		{
		s60v = static_cast<int> (s60ver.getversion() * 10);
		//![0]
		connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this,
				SLOT(handleNetworkReply(QNetworkReply*)));
		//![1]
		map = new QLightMaps(this);
		setCentralWidget(map);
		map->setFocus();

		//![1-1]
		s = new QSearch(map);
		connect(s, SIGNAL(closed()), this, SLOT(searchGSM()));

		if (s60v >= 50)
			{
			//![2]
			topbar = new QWidget(map);
			topbar->setStyleSheet(
					"background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
						"stop: 0 rgba(166, 205, 236, 220), stop: 0.4 rgba(136, 155, 255, 190),"
						"stop: 0.5 rgba(136, 155, 255, 160), stop: 1.0 rgba(166, 205, 236, 90));");
			topbar->move(QPoint(0, 0));
			}

		//![2-1]
		if (s60v >= 50)
			loading = new QAnimationLabel(":/data/wait.gif", topbar);
		else
			loading = new QAnimationLabel(":/data/wait.gif", map);
		loading->setStyleSheet("background: rgba(0, 0, 0, 0);");
		connect(map->m_normalMap, SIGNAL(started()), loading, SLOT(start()));
		connect(map->m_normalMap, SIGNAL(finished()), loading, SLOT(stop()));
		connect(map->m_largeMap, SIGNAL(started()), loading, SLOT(start()));
		connect(map->m_largeMap, SIGNAL(finished()), loading, SLOT(stop()));

		//![2-2]
		if (s60v >= 50)
			searchloading = new QAnimationLabel(":/data/search_wait.gif",
					topbar);
		else
			searchloading = new QAnimationLabel(":/data/search_wait.gif", map);
		searchloading->setStyleSheet("background: rgba(0, 0, 0, 0);");
		connect(map->m_normalMap, SIGNAL(started()), searchloading,
				SLOT(stop()));

		//![2-3]
		if (s60v >= 50)
			opname = new QLabel(topbar);
		else
			opname = new QLabel(map);
		opname->setFont(QFont("Georgia", 5));
		opname->setStyleSheet("background: rgba(0, 0, 0, 0);");

		if (s60v >= 50)
			{
			//![2-4]
			nightday = new QPushButton(map);
			connect(nightday, SIGNAL(clicked(bool)), map,
					SLOT(toggleNightMode()));
			nightday->setIconSize(QSize(iconsize, iconsize));
			nightday->setIcon(QIcon(":/data/nightday57.png"));
			nightday->setStyleSheet("border: 0px solid black;"
				"margin: 0;"
				"padding: 0;"
				"background: rgba(0, 0, 0, 0);");
			nightday->setToolTip(tr("Switch Day / Night Mode"));

			//![2-5]
			infoGSM = new QPushButton(map);
			connect(infoGSM, SIGNAL(clicked(bool)), s, SLOT(search()));
			infoGSM->setIconSize(QSize(iconsize, iconsize));
			infoGSM->setIcon(QIcon(":/data/antenna57.png"));
			infoGSM->setStyleSheet("border: 0px solid black;"
				"margin: 0;"
				"padding: 0;"
				"background: rgba(0, 0, 0, 0);");
			infoGSM->setToolTip(tr("Switch Day / Night Mode"));
			}
		else
			{
			//![2-4]
			zoom = new QPushButton(map);
			connect(zoom, SIGNAL(clicked(bool)), SLOT(zoomMap()));
			zoom->setIconSize(QSize(iconsize_small, iconsize_small));
			zoom->setIcon(QIcon(":/data/search32.png"));
			zoom->setStyleSheet("border: 0px solid black;"
				"margin: 0;"
				"padding: 0;"
				"background: rgba(0, 0, 0, 0);");
			zoom->setToolTip(tr("Zoom Map"));
			}

		//![3]
#if defined (Q_OS_SYMBIAN)
		sysinfo = new XQSysInfo;
		connect(sysinfo, SIGNAL(networkSignalChanged(ulong)), this,
				SLOT(readGSM()));
		opname->setText(sysinfo->operatorname());
		readGSM(true); // Write info GSM
#endif

		//![4]
		QAction *infoGSMAction = new QAction(tr("&Info GSM"), this);
		QAction *searchGSMAction = new QAction(tr("&Search GSM"), this);
		QAction *nightModeAction = new QAction(tr("&Night Mode"), this);
		QAction *osmAction = new QAction(tr("&About"), this);
		QAction *trafficAction = new QAction(tr("&Traffic Session"), this);
		connect(infoGSMAction, SIGNAL(triggered()), SLOT(readGSM()));
		connect(searchGSMAction, SIGNAL(triggered()), s, SLOT(search()));
		connect(nightModeAction, SIGNAL(triggered()), map,
				SLOT(toggleNightMode()));
		connect(osmAction, SIGNAL(triggered()), SLOT(aboutCellID()));
		connect(trafficAction, SIGNAL(triggered()), SLOT(trafficsession()));

		//![5]
#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
		menuBar()->addAction(infoGSMAction);
		menuBar()->addAction(searchGSMAction);
		menuBar()->addAction(nightModeAction);
		menuBar()->addAction(osmAction);
		menuBar()->addAction(trafficAction);
#else
		QMenu *menu = menuBar()->addMenu(tr("&Options"));
		menu->addAction(infoGSMAction);
		menu->addAction(searchGSMAction);
		menu->addAction(nightModeAction);
		menu->addSeparator();
		menu->addAction(osmAction);
		menu->addAction(trafficAction);
#endif

		//![6]
		QTimer::singleShot(0, this, SLOT(delayedInit()));

		//![7]
		version = "cellID Qt " + QString::number(
				static_cast<double> (cid_version), 'f', 1);
		}

protected:
	void resizeEvent(QResizeEvent *event)
		{
		Q_UNUSED(event);
		if (s60v >= 50)
			{
			//![1]
			topbar->resize(width(), 30);
			//![2] 
			infoGSM->move(QPoint(width() - (nightday->size().width() - padding)
					* 2 - padding, height() - nightday->size().height()
					- padding));
			nightday->move(QPoint(width()
					- (nightday->size().width() - padding) * 1 - padding,
					height() - nightday->size().height() - padding));
			}
		else
			{
			//![1]
			zoom->move(QPoint(width() - (zoom->size().width() - padding) * 1
					- padding, height() - zoom->size().height() - padding));
			}

		//![3]
		// [loading --------------- searchloading - opname]
		loading->move(QPoint(padding, padding));
		opname->move(QPoint(width() - opname->width() - padding, padding));
		searchloading->move(QPoint(width() - (opname->width() + padding)
				- (searchloading->width() + padding), padding));
		}

private slots:
	void trafficsession()
		{
		QMessageBox::information(map, tr("Traffic"), QString::number(
				map->getTraffic(), 'f', 2));
		}

	void zoomMap()
		{
		map->setZoom(!map->zoom());
		}

	void handleNetworkReply(QNetworkReply* reply)
		{
		QString temp;
		if (!reply->error())
			{
			temp.append(reply->readAll());
			parseXML(temp);
			}
		else
			{
			QMessageBox::critical(map, tr("Error"), tr("There was an error: ")
					+ reply->errorString());
			searchloading->stop();
			}
		reply->deleteLater();
		}

	void searchGSM() //![Opened by QSearch "close" signal]
		{
		QNetworkRequest request;
		QUrl url("http://www.cellid.eu/api.php");

		if (s->errors())
			{
			// QMessageBox::critical(map, tr("Error"), tr("There was an error when compile dialog."));
			return;
			}

		url.addQueryItem("mcc", s->getMCC());
		url.addQueryItem("mnc", s->getMNC());
		url.addQueryItem("lac", s->getLAC());
		url.addQueryItem("cid", s->getCID());

		url.addQueryItem("output", "xml");
#if defined (Q_OS_SYMBIAN)
		url.addQueryItem("device", version + " S60 " + QString::number(
				static_cast<double> (s60ver.getversion()), 'f', 1));
#else
		url.addQueryItem("device", version);
#endif

		request.setUrl(url);
		searchloading->start();
		m_manager.get(request);
		}

	void readGSM(bool first = false)
		{
#if defined (Q_OS_SYMBIAN)
		if (!first)
			QMessageBox::information(
					map,
					tr("GSM Info"),
					tr("MCC: %1\nMNC: %2\nLocal Area Code: %3\nCell ID: %4").arg(
							sysinfo->mcc()).arg(sysinfo->mnc()).arg(
							sysinfo->lac()).arg(sysinfo->cid()));
#endif
		}

	void delayedInit()
		{
#if defined(Q_OS_SYMBIAN)
		qt_SetDefaultIap();
#endif
		}

	void aboutCellID()
		{
		// QDesktopServices::openUrl(QUrl("http://www.cellid.eu"));
		QStringList iapNames;
		QString list;
		int error = qt_IapList(iapNames);
		if (error == KErrNone)
			{
			for (int index = 0; index < iapNames.size(); index++)
				{
				list += iapNames.at(index) + "\n";
				}
			}
		QMessageBox::about(this, QString::number(iapNames.size()), list);
		}

private:
	void parseXML(const QString &data)
		{
		QXmlStreamReader xml(data);
		while (!xml.atEnd())
			{
			xml.readNext();
			if (xml.tokenType() == QXmlStreamReader::StartElement)
				{
				if (xml.name() == "info")
					{
					if (QString(xml.attributes().value("found").toString()).toInt() == 1)
						{
						map->setCenter(
								xml.attributes().value("latitude").toString().toFloat(),
								xml.attributes().value("longitude").toString().toFloat(),
								xml.attributes().value("approx").toString().toInt());
						}
					else
						{
						QMessageBox::critical(map, tr("There was an error"),
								tr("GPS not found!"));
						searchloading->stop();
						}
					}
				}
			}
		}
	};

#include "QtcellID.moc"

int main(int argc, char **argv)
	{
#if defined(Q_WS_X11)
	QApplication::setGraphicsSystem("raster");
#endif

	QApplication app(argc, argv);
	app.setApplicationName("QtCellID");

	MapZoom w;
	w.setWindowTitle("cellID");
#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM)
	// w.showMaximized();
	w.setWindowFlags(w.windowFlags() ^ Qt::WindowSoftkeysVisibleHint);
	w.showFullScreen();
#else
	w.resize(600, 450);
	w.show();
#endif

	return app.exec();
	}
